﻿//----------------------------------------------------------------
// Desc: 
// Edit: ShowLong, http://blog.csdn.net/showlong
//
// Date Created: 2010-06-11
//
// Update Date : 
//
// Copyright (c) Fuel4D (http://www.fuel4d.com), All rights reserved.
//
//----------------------------------------------------------------
#pragma once


//----------------------------------------------------------------
// 引擎框架树及层次关系
//----------------------------------------------------------------
// + IFEngine;              // 引擎对象 <engine/app/graph/wnd/timer/state/env/res/obj/mod/world>
// | + IFApplication;       // 应用程序基类对象<oncreate/ondestory/onupdate/onrender/onenter/onleave/...>
// | + IFGraph;             // 图形对象 <d3d9/d3dx11/opengl>
// | + IFWnd;               // 窗体对象 <windows/x11>
// | + IFEnvMgr;            // 环境管理 <backcolor/specular/env_config/camera/sunlight/pointlite/fog>
// | | + IFEnvConfig;       // 环境设置 <RT config>
// | | + IFCamera;          // 摄像机   <view/flying/pick/frustum>
// | | + IFSunlight;        // 太阳设置 <dir/color>
// | | + IFPointolite;      // N点光源  <position/color/depth>
// | + IFResMgr;            // 资源管理 <factory/*.fmd/*.fsd/*.fad/*.fwd/*.fx/...>
// | | + IFModel;           // 模型管理 <*.fmd>
// | | + IFSprite;          // 精灵管理 <*.fsd>
// | | + IFTexture;         // 贴图管理 <*.png/*.dds/*.jpg/...>
// | + IFObjMgr;            // 对象管理 <animation/avatar/sprite(obj...)>
// | | + IFAnimation;       // 模型动画 <frame>
// | | + IFAvatar;          // 模型时装 <part>
// | | + IFSpriteCooling;   // CD精灵   <cd>
// | | + IFSpriteDistortion;// 扭曲精灵 <distortion>
// | | + IFSpriteParticle;  // 2D粒子   <particle>
// | | + IFSpriteTrack;     // 轨迹精灵 <track>
// | | + IFSpriteAnimate;   // 骨骼精灵 <animate>
// | + IFModMgr;            // 组件管理 <rander/font>
// | | + IFRender;          // 渲染器   <cache/render/commit/flags>
// | | + IFFont;            // 字体管理 <drawtext>
// | + IFWorldMgr;          // 世界管理 <load/update/render>
// | | + IFScene;           // 场景信息 <maplist/critter/map_property(pk/ride/single_duplicate/group_duplicate/...)>
// | | | + IFMap;           // 地图文件 <filelist/terrain/landscape/grass/flower/sceneobject(building)/oct>
// | | | | + IFTerrain;     // 地形信息 <terrain/trunk/grid/water/bst>



//----------------------------------------------------------------
//
//----------------------------------------------------------------
#include "Engine/IFService.h"           // 引擎服务对象
#include "Engine/IFEnvMgr.h"            // 环境管理
#include "Engine/IFResMgr.h"            // 资源管理
#include "Engine/IFObjMgr.h"            // 对象管理
#include "Engine/IFModMgr.h"            // 组件管理
#include "Engine/IFWorldMgr.h"          // 世界场景管理
#include "Engine/IFGraph.h"             // 图形对象
#include "Engine/IFWnd.h"               // 窗体对象


//----------------------------------------------------------------
//
//----------------------------------------------------------------
class IFApplication;
class IFEngine;
class IFGraph;
class IFWnd;


//----------------------------------------------------------------
// 引擎启动参数
//   1. 标有"(***)"类型参数为必填参数
//   2. 有些参数会为自动适应相关显卡、硬件等环境自动变化，初始化的值并不一定是最终值
//----------------------------------------------------------------
#pragma pack(push, 1)
struct FEngineParameter
{
    bool bUnicode;                      // 应用程序的字符集环境 (自动填写)

    // 与设备与图形相关设置
    struct _APP_GRAPH
    {
        EF_DEVICE_TYPE nDeviceType;     // 准备创建的渲染对象类型，如果失败将自动切到合适的环境 (***)
        DWORD nEnvType;                 // 运行环境设置（由 EF_ENV_TYPE 组合而成） (***)
        IFApplication* pApp;            // 目标运行主程序（必须由应用程序指定）(***)
        IFGraph* pDstGraph;             // 图形渲染（如果不是自定义的，则使用nDeviceType自动创建）
        IFWnd* pDstWnd;                 // 窗口设备（由App框架来实现这个）

        EF_FORMAT nBackFmt;             // 后备缓冲区格式
        EF_FORMAT nDepthFmt;            // 深度模板缓冲格式(深度模板默认必须开启)
        bool bVerticalSync;             // 是否开启垂直同步（与nLockFps同时设置时，按最小的设置FPS处理）
        UINT nLockFps;                  // 锁定FPS在多少帧（0表示不锁帧，大于0表示锁定FPS在该帧）
        bool bEnableMipmap;             // 启用mipmap功能
        bool bEnableBackLock;           // 启用后背缓冲区锁定功能(开启后将自动关闭nMultiSample功能)
        //EF_MULTI_SAMPLE nMultiSample;   // 期望的最大采样极别
        bool bFPUDouble;                // FPU使用double精度
        bool bClearBackWhenSwap;        // 当后备缓冲区翻转后，是否丢弃(如果需要支持DirtyRegion则此值需要设为false)
        EF_FORMAT nMRT;                 //

        bool b3DInit;                   // 是否初始化3D相关数据
        EF_CLEAR_FLAGS nClearFlags;     // 默认的清理标记
        DWORD nComitFlags;              // 默认缓冲区提标记（EF_RENDER_COMMIT_FLAGS）

        _APP_GRAPH()
            :nDeviceType(eDT_D3D9)
            ,nEnvType(eET_2D_Shader)
            ,pApp(0)
            ,pDstGraph(0)
            ,pDstWnd(0)
            ,nBackFmt(eFMT_X8R8G8B8)
            ,nDepthFmt(eFMT_D16)
            ,bVerticalSync(true)
            ,nLockFps(0)
            ,bEnableMipmap(false)
            ,bEnableBackLock(false)
            //,nMultiSample(eMS_Mone)
            ,bFPUDouble(false)
            ,bClearBackWhenSwap(true)
            ,nMRT(eFMT_A32B32G32R32F)
            ,b3DInit(false)
            ,nClearFlags(eCF_T_Z)
            ,nComitFlags(eRCF_2D)
        {}
    } graph;

    // 窗体设置
    struct _APP_WINDOW
    {
        ACHAR szWindow[MAX_PATH];       // 窗口标题或者应用标题
        bool bWindow;                   // 是否是窗口模式运行
        WORD nWidth;                    // 渲染区宽度
        WORD nHeight;                   // 渲染区高度
        WORD nMDWidth;                  // 移动设备实际渲染宽度
        WORD nMDHeight;                 // 移动设备实际渲染高度
        WORD nMinWidth;                 // 窗口允许支持的最小渲染区宽度
        WORD nMinHeight;                // 窗口允许支持的最小渲染区高度
        WORD nMaxWidth;                 // 窗口允许支持的最大渲染区宽度
        WORD nMaxHeight;                // 窗口允许支持的最大渲染区高度

        _APP_WINDOW()
            :bWindow(true)
            ,nWidth(640)
            ,nHeight(480)
            ,nMDWidth(640)
            ,nMDHeight(480)
            ,nMinWidth(240)
            ,nMinHeight(240)
            ,nMaxWidth(8192)
            ,nMaxHeight(8192)
        {
            FMemzero(szWindow, sizeof(szWindow));
        }
        void CompMDHeight()
        {
#if FUEL_OS_ANDROID
            //if (nWidth < nMDWidth)
            //    nMDWidth = nWidth;

            double kv = (double)nWidth / (double)nMDWidth;
            nMDHeight = (WORD)((double)nHeight / kv);
#else
            nMDWidth = nWidth;
            nMDHeight = nHeight;
#endif
        }
    } wnd;

    // 字体相关设置
    struct _APP_FONT
    {
        ACHAR fntName[MAX_PATH];        // 默认字体文件名
        BYTE nSize;                     // 默认字体大小
        bool bBold;                     // 默认字体是否粗体
        bool bSmooth;                   // 默认字体是否边缘平滑

        _APP_FONT()
            :nSize(14)
            ,bBold(false)
            ,bSmooth(true)
        {
            FMemzero(fntName, sizeof(fntName));
        }
    } fnt;

    // windows 操作系统平台相关参数
    struct _OS_WINDOWS
    {
        HINSTANCE hInstance;            // 窗口实例
        HINSTANCE hPrevInstance;        // 由WINMAIN传递
        int nShowCmd;                   // 由WINMAIN传递
        HWND hHwnd;                     // 目标运行和渲染句柄窗口
        DWORD nIconID;                  // 图标资源ID
        DWORD nCursorID;                // 鼠标资源ID
        DWORD nMenuID;                  // 菜单资源
        bool bWndMaxbox;                // 窗口模式下是否显示并启用最大化按钮（如果不显示，那么窗体也不能随意拖动大小）
        bool bWndTitle;                 // 窗口模式下是否显示标题栏
        bool bMoveWindowNoChoke;        // 是否启用移动窗体时不阴塞(阻塞时标题栏将禁止用标题系统菜单功能，但不能阻塞窗口尺寸变化)
        bool bHideWindow;               // 不要立即显示窗口（需要 自己调用ShowWindow和UpdateWindow）
        bool bNoTitleMove;              // 点击窗体任意位置移动窗口

        _OS_WINDOWS()
            :hInstance(0)
            ,hPrevInstance(0)
            ,nShowCmd(0)
            ,hHwnd(0)
            ,nIconID(0)
            ,nCursorID(0)
            ,nMenuID(0)
            ,bWndMaxbox(true)
            ,bWndTitle(true)
            ,bMoveWindowNoChoke(true)
            ,bHideWindow(false)
            ,bNoTitleMove(false)
        {}
    } os_windows;

    // android 操作系统平台相关参数
    struct _OS_ANDROID
    {
        _OS_ANDROID()
        {}
    } os_android;

    // ios 操作系统平台相关参数
    struct _OS_IOS
    {
        _OS_IOS()
        {}
    } os_ios;


    // 其它辅助设置
    struct _APP_ASSIST
    {
        bool bEnablePerfHUD;            // 启动NV的PerfHUD功能

        _APP_ASSIST()
            :bEnablePerfHUD(true)
        {}
    } assist;

    FEngineParameter()
    {
#ifdef UNICODE
        bUnicode = true;
#else
        bUnicode = false;
#endif

        // 默认以2D渲染环境处理
        SetFont(_FTA("../assets/font/dsf.ttf"));
        Set2DEnv();
    }
    FINLINE void AddEnvType(DWORD prop)
    {
        graph.nEnvType |= prop;
    }
    FINLINE void SubEnvType(DWORD prop)
    {
        graph.nEnvType &= ~prop;
    }
    FINLINE bool HasEnvType(DWORD prop)
    {
        return (graph.nEnvType & prop) != 0;
    }
    FINLINE void SetEnvType(DWORD prop, bool vl)
    {
        if (vl)
            AddEnvType(prop);
        else
            SubEnvType(prop);
    }
    FINLINE void SetTitle(const ACHAR* title)
    {
        if (title && *title)
            FStrcpyA(wnd.szWindow, title);
    }
    FINLINE void SetWindow(WORD width, WORD height, bool window = true, const ACHAR* title = 0)
    {
        wnd.nWidth  = width;
        wnd.nHeight = height;
        wnd.nMDWidth  = width;
        wnd.nMDHeight = height;
        wnd.bWindow = window;
        SetTitle(title);
        wnd.CompMDHeight();
    }
    FINLINE void SetFont(const ACHAR* fontname, BYTE fontsize = 14, bool bblod = false, bool bsmooth = true)
    {
        if (fontname && *fontname)
            FStrcpyA(fnt.fntName, fontname);
        fnt.nSize   = fontsize;
        fnt.bBold   = bblod;
        fnt.bSmooth = bsmooth;
    }
    FINLINE void Set2DEnv(EF_DEVICE_TYPE type = eDT_UNKNOWN)
    {
        graph.nDeviceType         = AutoDeviceType(type);
        graph.nBackFmt            = eFMT_X8R8G8B8;
        graph.nDepthFmt           = eFMT_D16;
        graph.bEnableMipmap       = false;
        graph.nClearFlags         = eCF_T_Z;
        graph.nComitFlags         = eRCF_2D;
        graph.b3DInit             = false;
        AddEnvType(eET_2D_Shader);
    }
    FINLINE void Set3DEnv(EF_DEVICE_TYPE type = eDT_UNKNOWN, DWORD prop = eET_2D_Shader|eET_3D_Shader_Model|eET_3D_World)
    {
        graph.nDeviceType         = AutoDeviceType(type);
        graph.nBackFmt            = eFMT_A8R8G8B8;
        graph.nDepthFmt           = eFMT_D24X8;
        graph.bEnableMipmap       = true;
        graph.nClearFlags         = eCF_T_Z;
        graph.nComitFlags         = eRCF_All;
        graph.b3DInit             = true;
        AddEnvType(prop);
    }
    FINLINE void SetOS_windows(IFApplication* app, HWND hwnd = 0, DWORD icon = 0, DWORD cursor = 0)
    {
        graph.pApp = app;
        os_windows.nIconID   = icon;
        os_windows.hHwnd     = hwnd;
        os_windows.nCursorID = cursor;
    }
    FINLINE EF_DEVICE_TYPE AutoDeviceType(EF_DEVICE_TYPE type)
    {
        if (type == eDT_UNKNOWN)
        {
#if FUEL_OS_WINDOWS
            type = eDT_D3D9;
#elif FUEL_OS_ANDROID
            type = eDT_GLES2;
#elif FUEL_OS_IOS
            type = eDT_GLES2;
#endif
        }
        return type;
    }
};
#pragma pack(pop)


//----------------------------------------------------------------
//
//----------------------------------------------------------------
class IFApplication : public IFMemObject
{
    friend class FEngine;

    FDECLARE_CLASS(IFApplication)

private:
    bool bInit;

protected:
    IFApplication()
        :bInit(false)
        ,fge(0)
    {}
    virtual~IFApplication()
    {}

public:
    // 由引擎告诉你的当前引擎地址
    IFEngine* fge;

    // 是否被引擎成功加载
    virtual bool IsInit(){ return bInit; }
    // 创建用户数据，引擎启动成功后调用此函数
    // .return 是否创建成功
    virtual bool Create(){ return true; }
    // 即将消毁程序（如果Create函数失败也会来此函数）
    virtual void Destroy(){}
    // 刷新函数
    virtual void Update(){}
    // 绘制函数
    // .返回是否提交缓冲区数据（如果你主动调用过IFRender的Commit，则返回false）
    virtual void Render() = 0;
    // 设备丢失时调用此函数
    virtual void Lost(){}
    // 设备重置时调用此函数
    virtual void Reset(){}
    // 由IFEngine::Switch切换状态请求，进入某个状态（不是每个游戏都需要通过状态切换游戏机制功能）
    // .st 当前要进入的状态号（st也可将某个状态的状态类指针传递进来，eCD_UnknownState表示无效状态）
    virtual void Enter(DWORD st){}
    // 由IFEngine::Switch切换状态请求，离开某个状态
    // .st 当前要离开的状态号
    virtual void Leave(DWORD st){}
    // 事件输入接口
    virtual bool OnInput(FINPUT_PACKAGE& ipk) { return false; }
    // 当 IFEngine->Initialize 成功后调用，晚于App->Create
    virtual bool OnAppInit(){ return true; }
    // 当 IFEngine->Run 主消息循环结束后调用，晚于App->Destroy
    virtual void OnAppEnd(){}
    // 请求退出游戏时调用此接口，WINDOWS的窗关闭，安卓的返回。
    // .return 返回true表示同意退出，返回false表示不能退出
    virtual bool OnReqQuit(){ return true; }
};


//----------------------------------------------------------------
//
//----------------------------------------------------------------
class IFEngine : public IFMemObject
{
protected:
    explicit IFEngine(FIDENGINE id)
        :nEngineID(id)
    {}

public:
    const FIDENGINE nEngineID;

    // 初始化引擎
    // 注意：如果内部初始化失败会自动抛出提示或打印到文件
    virtual bool Initialize(FEngineParameter& param) = 0;
    // 运行引擎
    //   如果FEngineParameter::os_windows::hHwnd在IFEngine::Initialize之前被指定以后
    //   那么此Run函数是非阻塞的，即需要在自己的主循环中不停地调用Run接口
    //   否则os_windows::hHwnd未指定情况下，此Run函数是阻塞的，由APP框架窗口对象内部自动循环
    virtual bool Run() = 0;
    // 释放引擎
    //   一般情况下不调用此接口，因为在调用FDelEngine的时候会自动调用此接口
    virtual void Release() = 0;
    // 切换分辨率到新尺寸，窗口跟着改
    virtual bool ChangeWndSize(size_t width, size_t height, bool center) = 0;
    // 切换状态（常规的状态机处理逻辑，单线模式）
    // .st 目标程序状态值，默认eCD_UnknownState表示没有状态功能
    //     指针也作强转为DWORD进行传递
    //     当条件满足以后，会先调用IFApplication::Leave，再调用IFApplication::Enter接口，
    virtual void Switch(DWORD st) = 0;
    // 获取当前程序状态
    virtual DWORD State() = 0;
    // 请求退出程序
    virtual void Quit() = 0;
    // 取得主工作应用程序
    virtual IFApplication* GetMainApp() = 0;
    // 取得图形设备对象
    virtual IFGraph* GetGraph() = 0;
    // 取得窗体对象
    virtual IFWnd* GetWnd() = 0;
    // 取得环境管理器
    virtual IFEnvMgr* GetEnvMgr() = 0;
    // 取得资源管理器
    virtual IFResMgr* GetResMgr() = 0;
    // 取得对象管理器
    virtual IFObjMgr* GetObjMgr() = 0;
    // 取得组件管理器
    virtual IFModMgr* GetModMgr() = 0;
    // 取得世界管理器
    virtual IFWorldMgr* GetWorldMgr() = 0;
    // 取得鼠标X
    virtual float GetCursorX() = 0;
    // 取得鼠标Y
    virtual float GetCursorY() = 0;
    // 取得鼠标坐标
    virtual const FVECTOR2* const GetCursor() = 0;
    // 当前设备是否已经丢失
    virtual bool IsDeviceLost() = 0;
    // 引擎是否初始化完成，包括用户初始化
    virtual bool IsInitFinished() = 0;
    // 取得当前FPS
    virtual float GetFPS() = 0;
    // 取得当前时间
    virtual double GetTime() = 0;
    // 取得当前帧流逝
    virtual float GetElapsedTime() = 0;
    // 绑定一个处理服务
    //     如果是new出来的服务，则需要在结束的时候调用Detach，全局的则不需要
    //     在引擎初始化之前调用，则统一在初始引擎的时候一起调用p->Initialize，且先于App->Create
    //     在引擎初始化之后调用，否则立即调用p->Initialize
    //     在引擎结束的时候，且在App->Destroy之后自动调用p->Release
    // .p 指向一个动态NEW出来的或者全局服务对象地址
    virtual void Attach(IFService* p) = 0;
    // 解绑一个处理服务
    //     解绑的时候会主动调用p->Release();
    // .del_it 解绑的时候调用FDel删除它
    virtual void Detach(IFService* p, bool del_it) = 0;
    // 查找一个服务对象（非高效函数，尽量少用）
    virtual IFService* FindService(const ACHAR* class_name) = 0;
    // 整理当前内存
    virtual void TidyMemory() = 0;
    // 给所有服务调用一个事件处理（详见EF_SERVE_EVENT）
    virtual void DoEvent(DWORD e, DWORD_PTR p0 = 0, DWORD_PTR p1 = 0) = 0;
    // 测试当前引擎渲染器运行标记
    virtual bool HasEnvType(EF_ENV_TYPE et) = 0;


    //----------------------------------------------------------------
    // 以下是直接对象访问
    //----------------------------------------------------------------
    // 获取摄像机对象
    FINLINE IFCamera* GetCamera(){ return GetEnvMgr()->GetCamera(); }
    // 获取太阳参数
    FINLINE IFSunlight* GetSunlight(){ return GetEnvMgr()->GetSunlight(); }
    // 获取雾参数
    FINLINE IFFog* GetFog(){ return GetEnvMgr()->GetFog(); }
    // 获取引擎环境设置
    FINLINE IFEnvConfig* GetEnvConfig(){ return GetEnvMgr()->GetEnvConfig(); }
    // 获取贴图资源
    FINLINE IFTexture* GetTexture(){ return GetResMgr()->GetTexture(); }
    // 获取精灵资源
    FINLINE IFSprite* GetSprite(){ return GetResMgr()->GetSprite(); }
    // 获取模型资源
    FINLINE IFModel* GetModel(){ return GetResMgr()->GetModel(); }
    // 获取渲染器组件
    FINLINE IFRender* GetRender(){ return GetModMgr()->GetRender(); }
    // 获取字体组件
    FINLINE IFFont* GetFont(){ return GetModMgr()->GetFont(); }
    // 获取窗体句柄
    FINLINE HWND GetHwnd(){ return GetWnd() ? GetWnd()->GetHwnd() : 0; }
    // 获取进程句柄
    FINLINE HINSTANCE GetHinstance(){ return GetWnd() ? GetWnd()->GetHinstance() : 0; }
    // 获取当前渲染区分辩率
    FINLINE DWORD GetWidthI(){ return (DWORD)GetParam()->wnd.nMDWidth; }
    FINLINE DWORD GetHeightI(){ return (DWORD)GetParam()->wnd.nMDHeight; }
    FINLINE float GetWidthF(){ return (float)GetParam()->wnd.nMDWidth; }
    FINLINE float GetHeightF(){ return (float)GetParam()->wnd.nMDHeight; }
    // 获取启动参数（不要修改）
    virtual const FEngineParameter* const GetParam() = 0;

    //----------------------------------------------------------------
    // 以下是供窗体对象逻辑调用的
    //----------------------------------------------------------------
    // 用于应用程序框架主循环调用的单帧处理，自主控制的主循环请调用IFEngine::Run
    virtual void FrameProcess() = 0;
    // 设备丢失时调用
    virtual bool OnLostDevice(bool force_lost) = 0;
    // 设置恢复时调用
    virtual bool OnResetDevice(bool init_reset) = 0;
    // 设置当前鼠标的位置，此函数自动更新pGraph的鼠标位置
    virtual void UpdateCursor(long x, long y) = 0;
};


// 创建Fuel4D引擎对象
extern FAPI IFEngine* FNewEngine();
// 调用引擎的Release，并删除Fuel4D引擎对象
extern FAPI void FDelEngine(IFEngine*& engine);
